package cn.yx.common.security.webflux.configuration;

import cn.yx.common.security.properties.SecurityProperties;
import cn.yx.common.security.service.JwtUserDetailsService;
import cn.yx.common.security.webflux.filter.JwtAuthenticationFilter;
import cn.yx.common.security.webflux.filter.VerifyCodeAuthenticationFilter;
import cn.yx.common.security.webflux.handler.CustomAccessDeniedHandler;
import cn.yx.common.security.webflux.handler.CustomAuthenticationEntryPoint;
import cn.yx.common.security.webflux.handler.CustomAuthenticationFailureHandler;
import cn.yx.common.security.webflux.handler.CustomAuthenticationSuccessHandler;
import cn.yx.common.security.webflux.manager.CustomReactiveAuthorizationManager;
import cn.yx.common.security.webflux.repository.CustomSecurityContextRepository;
import cn.yx.common.security.webflux.sms.SmsCodeAuthenticationFilter;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.LinkedList;

/**
 * <p>SecurityConfiguration配置类</p>
 *
 * @author Wgssmart
 */
@Configuration
@EnableWebFluxSecurity
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityConfiguration {

    private final SecurityProperties securityProperties;

    @Resource
    private JwtUserDetailsService jwtUserDetailsService;

    @Resource
    @Lazy
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Resource
    private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;

    @Resource
    private CustomAuthenticationFailureHandler customAuthenticationFailureHandler;

    @Resource
    private CustomAccessDeniedHandler customAccessDeniedHandler;

    @Resource
    @Lazy
    private CustomReactiveAuthorizationManager customReactiveAuthorizationManager;

    @Resource
    @Lazy
    private CustomSecurityContextRepository customSecurityContextRepository;

    @Resource
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Resource
    private VerifyCodeAuthenticationFilter verifyCodeAuthenticationFilter;

    @Resource
    @Lazy
    private SmsCodeAuthenticationFilter smsCodeAuthenticationFilter;

    public SecurityConfiguration(SecurityProperties securityProperties) {
        this.securityProperties = securityProperties;
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return web -> web.ignoring().antMatchers("/", "/wx-auth/**");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(authentication -> {
            // 其他登陆方式 (比如手机号验证码登陆) 可在此设置不得抛出异常或者 Mono.error
            return Mono.empty();
        });
        managers.add(new UserDetailsRepositoryReactiveAuthenticationManager(jwtUserDetailsService));
        return new DelegatingReactiveAuthenticationManager(managers);
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
        return httpSecurity
                .cors()
                .and()
                .addFilterBefore(verifyCodeAuthenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .authenticationManager(reactiveAuthenticationManager())
                // .securityContextRepository(customSecurityContextRepository)
                .authorizeExchange(exchange -> exchange.anyExchange().access(customReactiveAuthorizationManager))
                .formLogin()
                .authenticationSuccessHandler(customAuthenticationSuccessHandler)
                .authenticationFailureHandler(customAuthenticationFailureHandler)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                .and()
                .logout()
                .and()
                .exceptionHandling()
                .accessDeniedHandler(customAccessDeniedHandler)
                .and()
                .csrf()
                .disable()
                .build();
    }

}
